from webargs import fields, ValidationError

from webutils import api_ngx_magic
from webutils.webargs.bare import use_args
from webutils.datetime import current_datetime
from webutils.tornado.responses import Response
from webutils.marshmallow.fields import CsvField
from .. import const, params, ratelimit, services


class ChatMixin:
    @use_args({
        'from': params.Sender(required=True),
        'msg': fields.Dict(required=True),
    })
    @ratelimit.ratelimit('100/1m', key='user')
    async def on_act_text_chat(self, args, conn):
        """
        """
        user_info = await self.get_user_info(
            args['from'],
            conn=conn,
            extra_keys=['group'],
        )
        chat_service = services.ChatService(
            conn,
            room_id=self.session.room_id,
        )
        is_blocked = await chat_service.is_user_blocked(
            user_info['id'], user_info['group'])
        if is_blocked:
            self.logger.info('[text_chat] user is blocked')
            return
        msg_content = args['msg'].get('c', '')
        if len(msg_content) > 500:
            self.logger.info('[text_chat] msg too long')
        msg_etag = args['msg'].get('etag', '')
        user_id = user_info['id']
        name = user_info['name']
        chat_message = {
            'act': const.ActType.TEXT_CHAT.value,
            'from': f'{user_id}:{name}',
            'msg': {
                'c': msg_content,
                'tag': msg_etag,
                't': 1,
            },
            'created_at': current_datetime(),
        }
        # save message
        chat_id = await chat_service.append_text_message(chat_message)
        chat_message['chat_id'] = chat_id
        chat_message['total'] = chat_id
        # broadcast
        await self.broadcaster_app.send_task(
            'broadcast_chat',
            chat_message,
            group=const.UserGroup.ALL,
        )
        resp = Response(
            stat=1,
            act=str(const.ActType.MESSAGE_ACK.value),
            type=1,
            etag=msg_etag,
            chat_id=chat_id,
        )
        await self.write_response(resp)

    @use_args({
        'from': params.Sender(required=True),
        'msg': fields.Dict(required=True),
    })
    @ratelimit.ratelimit('100/1m', key='user')
    async def on_act_image_chat(self, args, conn):
        """图片聊天
        """
        user_info = await self.get_user_info(
            args['from'],
            conn=conn,
            extra_keys=['group'],
        )
        chat_service = services.ChatService(
            conn,
            room_id=self.session.room_id,
        )
        is_blocked = await chat_service.is_user_blocked(
            user_info['id'], user_info['group'])
        if is_blocked:
            self.logger.info('[image_chat] user is blocked')
            return
        msg_content = args['msg'].get('msg', '')
        if not msg_content:
            msg_content = args['msg'].get('c', '')
        msg_etag = args['msg'].get('etag', '')
        user_id = user_info['id']
        name = user_info['name']
        chat_message = {
            'act': str(const.ActType.IMAGE_CHAT.value),
            'from': f'{user_id}:{name}',
            'msg': {
                'c': msg_content,
                'etag': msg_etag,
                't': 2
            },
            'create_at': current_datetime(),
        }
        # save message
        chat_id = await chat_service.append_image_message(chat_message)
        chat_message['chat_id'] = chat_id
        chat_message['total'] = chat_id
        # broadcast
        await self.broadcaster_app.send_task(
            'broadcast_chat',
            chat_message,
            group=const.UserGroup.ALL,
        )
        resp = Response(
            stat=1,
            act=const.ActType.MESSAGE_ACK.value,
            type=2,  # 随便写的一个数字
            etag=msg_etag,
            chat_id=chat_id,
        )
        await self.write_response(resp)

    @use_args({
        'type': fields.Str(required=True),
        'groups': CsvField(cast_func=str, delimiter='|', missing=[]),
        'members': fields.List(fields.Dict, missing=[]),
    })
    async def on_act_chat_blacklist(self, args, conn):
        """聊天黑名单
        """
        BLOCK, UNBLOCK = 1, 2
        try:
            groups = list(map(const.UserGroup, args['groups']))
        except ValueError:
            raise ValidationError(f'invalid groups: {args["groups"]}')
        try:
            members = {item['id']: item for item in args['members']}
        except KeyError:
            raise ValidationError(f'invalid members: {args["members"]}')
        chat_service = services.ChatService(
            conn,
            room_id=self.session.room_id,
        )
        if api_ngx_magic.equals(args['type'], BLOCK):
            for user_id, user_info in members.items():
                await chat_service.block_user(user_id, user_info=user_info)
            for group in groups:
                await chat_service.block_group(group)
        elif api_ngx_magic.equals(args['type'], UNBLOCK):
            for user_id, user_info in members.items():
                await chat_service.unblock_user(user_id)
            for group in groups:
                await chat_service.unblock_group(group)
        else:
            raise ValidationError('invalid type: {args["type"]}')

        message = {
            'act': str(const.ActType.CHAT_BLACKLIST.value),
            'created_at': current_datetime(),
            'type': args['type'],
        }
        if groups:
            message['groups'] = args['groups']
        if members:
            message['members'] = args['members']
        await self.broadcaster_app.send_task(
            'broadcast_chat_blacklist',
            message,
            group=const.UserGroup.ALL,
        )

    @use_args({
        'from': params.Sender(required=True),
        'msg': fields.Dict(required=True),
    })
    async def on_act_emoji_chat(self, args, conn):
        """表情聊天
        """
        user_info = await self.get_user_info(
            args['from'],
            conn=conn,
            extra_keys=['group'],
        )
        chat_service = services.ChatService(
            conn,
            room_id=self.session.room_id,
        )
        is_blocked = await chat_service.is_user_blocked(
            user_info['id'], user_info['group'])
        if is_blocked:
            self.logger.info('[text_chat] user is blocked')
            return
        msg_content = args['msg'].get('c', '')
        if len(msg_content) > 500:
            self.logger.info('[text_chat] msg too long')
        user_id = user_info['id']
        name = user_info['name']
        chat_message = {
            'act': const.ActType.EMOJI_CHAT.value,
            'from': f'{user_id}:{name}',
            'msg': {
                'e': msg_content,
                'n': 1,
            },
            'created_at': current_datetime(),
        }
        # save message
        chat_id = await chat_service.append_text_message(chat_message)
        chat_message['chat_id'] = chat_id
        chat_message['total'] = chat_id
        # broadcast
        await self.broadcaster_app.send_task(
            'broadcast_chat',
            chat_message,
            group=const.UserGroup.ALL,
        )
        resp = Response(
            stat=1,
            act=str(const.ActType.MESSAGE_ACK.value),
            type=1,
            chat_id=chat_id,
        )
        await self.write_response(resp)
